package gogo

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"net/http"
	"strconv"
	"strings"

	"go.mongodb.org/mongo-driver/bson"
)

// HTTPContext 提供给客户使用的扩展指针，包含了原始的http.ResponseWriter和http.Request，同时还提供了封装好的一系列方便函数
type HTTPContext struct {
	w         http.ResponseWriter
	r         *http.Request
	urlParams map[string]string
	body      []byte
}

// GetW 获取原始的http.ResponseWriter
func (ctx *HTTPContext) GetW() *http.ResponseWriter {
	return &ctx.w
}

// GetR 获取原始的http.Request
func (ctx *HTTPContext) GetR() *http.Request {
	return ctx.r
}

// GetResponseWriter 获取原始的http.ResponseWriter
func (ctx *HTTPContext) GetResponseWriter() http.ResponseWriter {
	return ctx.w
}

// GetRequest 获取原始的http.Request
func (ctx *HTTPContext) GetRequest() *http.Request {
	return ctx.r
}

// GetPostBody 获取POST请求的内容
func (ctx *HTTPContext) GetPostBody() []byte {
	if ctx.body == nil {
		ctx.body, _ = io.ReadAll(ctx.r.Body)
		ctx.r.Body.Close()
	}

	return ctx.body
}

// GetString 获取GET请求?后面的String参数值,
// key string: 参数名称
// def ...string: 参数可选，当获取参数值无效的时候，便返回这里提供的值
func (ctx *HTTPContext) GetString(key string, def ...string) string {
	strv := strings.Join(ctx.r.Form[key], "")
	if len(strv) == 0 && len(def) > 0 {
		return def[0]
	}
	return strv
}

// GetInt 获取GET请求?后面的int参数值
// key string: 参数名称
// def ...int: 参数可选，当获取参数值无效的时候，便返回这里提供的值
func (ctx *HTTPContext) GetInt(key string, def ...int) int {
	strv := strings.Join(ctx.r.Form[key], "")
	if len(strv) == 0 && len(def) > 0 {
		return def[0]
	}

	if result, err := strconv.Atoi(strv); err == nil {
		return result
	}
	return 0
}

// GetInt64 获取GET请求?后面的int64参数值
// key string: 参数名称
// def ...int64: 参数可选，当获取参数值无效的时候，便返回这里提供的值
func (ctx *HTTPContext) GetInt64(key string, def ...int64) int64 {
	strv := strings.Join(ctx.r.Form[key], "")
	if len(strv) == 0 && len(def) > 0 {
		return def[0]
	}

	if result, err := strconv.ParseInt(strv, 10, 64); err == nil {
		return result
	}
	return 0
}

// GetPathParam 获取GET请求路径中的String格式参数值，例如/restful/:table_name/:id
// key string: 参数名称
func (ctx *HTTPContext) GetPathParam(key string) string {
	return ctx.urlParams[key]
}

// WriteHeaderStatus 指定Status到头部输出给HTTP客户端
// status int: HTTP状态, 参考net\http\status.go
func (ctx *HTTPContext) WriteHeaderStatus(status int) {
	ctx.w.WriteHeader(status)
}

// WriteHeader 将String格式的value设置到Header头部key字段
func (ctx *HTTPContext) WriteHeader(key, value string) {
	ctx.w.Header().Set(key, value)
}

// WriteHeaderContentType 返回信息类型Content-Type
func (ctx *HTTPContext) WriteHeaderContentType(value string) {
	ctx.WriteHeader("Content-Type", value)
}

// WriteString 将String格式的数据输出给HTTP客户端
// content string: 需要输出HTTP客户端的String格式数据
func (ctx *HTTPContext) WriteString(content string) (int, error) {
	return ctx.w.Write([]byte(content))
}

// WriteByte 将[]byte格式的数据输出给HTTP客户端
// content []byte: 需要输出HTTP客户端的[]byte格式数据
func (ctx *HTTPContext) WriteByte(content []byte) (int, error) {
	return ctx.w.Write(content)
}

// WriteJSON 将Struct结构体的数据转换成Json输出给HTTP客户端
// v interface{}: 需要输出HTTP客户端的Struct结构体数据
func (ctx *HTTPContext) WriteJSON(v interface{}) error {
	b, err := json.Marshal(v)
	if err == nil {
		ctx.WriteByte(b)
		return nil
	}

	ctx.WriteString(err.Error())
	return err
}

// WriteBSON 将Struct结构体的数据转换成Bson输出给HTTP客户端
// v interface{}: 需要输出HTTP客户端的Struct结构体数据
func (ctx *HTTPContext) WriteBSON(v interface{}) error {
	b, err := bson.Marshal(v)
	if err == nil {
		ctx.WriteByte(b)
		return nil
	}

	ctx.WriteString(err.Error())
	return err
}

// WriteExecute 将官方原始的ParseFiles和Execute接口做了合并
// data interface{}: 渲染模板需要的数据
// filenames ...string: 模板文件路径
func (ctx *HTTPContext) WriteExecute(data interface{}, filenames ...string) error {
	tmpl, err := template.ParseFiles(filenames...)

	if err == nil {
		tmpl.Execute(ctx.w, data)
		return nil
	}

	return err
}

func (ctx *HTTPContext) WriteParse(data interface{}, tmplContents ...string) error {
	var t *template.Template

	if len(tmplContents) == 0 {
		// Not really a problem, but be consistent.
		return fmt.Errorf("html/template: no files named in call to ParseFiles")
	}
	for num, s := range tmplContents {
		name := strconv.Itoa(num)
		// First template becomes return value if not already defined,
		// and we use that one for subsequent New calls to associate
		// all the templates together. Also, if this file has the same name
		// as t, this file becomes the contents of t, so
		//  t, err := New(name).Funcs(xxx).ParseFiles(name)
		// works. Otherwise we create a new template associated with t.
		var tmpl *template.Template
		if t == nil {
			t = template.New(name)
			tmpl = t
		} else {
			tmpl = t.New(name)
		}
		_, err := tmpl.Parse(s)
		if err != nil {
			return err
		}
	}

	return t.Execute(ctx.w, data)
}

// Redirect 网页跳转
// url string: 跳转的url
// status int: 需要写入头部的状态，例如http.StatusOK
func (ctx *HTTPContext) Redirect(url string, status int) {
	http.Redirect(ctx.w, ctx.r, url, status)
}

// 添加不缓存的头部信息
func (ctx *HTTPContext) AddHeaderForNoCache() {
	HTTPAddHeaderForNoCache(ctx.w)
}

// 根据下载文件类型添加头部信息
func (ctx *HTTPContext) AddHeaderForSuffix(suffix string) {
	HTTPAddHeaderForSuffix(ctx.w, suffix)
}

// GetBasicAuthUserNameAndPasswd 获取Basic认证的用户名和密码
func (ctx *HTTPContext) GetBasicAuthUserNameAndPasswd() (string, string) {
	auth := ctx.r.Header.Get("Authorization")
	if auth == "" {
		return "", ""
	}

	// 检查是否是Basic认证
	if !strings.HasPrefix(auth, "Basic ") {
		return "", ""
	}

	// 解码认证信息
	encoded := strings.TrimPrefix(auth, "Basic ")
	decoded, err := base64.StdEncoding.DecodeString(encoded)
	if err != nil {
		return "", ""
	}

	// 分离用户名和密码
	credentials := strings.SplitN(string(decoded), ":", 2)
	if len(credentials) != 2 {
		return "", ""
	}

	return credentials[0], credentials[1]
}

// 添加不缓存的头部信息
func HTTPAddHeaderForNoCache(w http.ResponseWriter) {
	w.Header().Add("Cache-Control", "no-cache, no-store, must-revalidate")
	w.Header().Add("Pragma", "no-cache")
	w.Header().Add("Expires", "-1")
}

// 文件后缀到Content-Type的映射表 - 使用map优化性能
var contentTypeMap = map[string]string{
	"js":        "application/javascript",
	"css":       "text/css",
	"html":      "text/html",
	"htm":       "text/html",
	"png":       "image/png",
	"svg":       "image/svg+xml",
	"ico":       "image/x-icon",
	"cur":       "image/x-icon",
	"json":      "application/json",
	"map":       "application/json",
	"woff":      "font/woff",
	"woff2":     "font/woff2",
	"ttf":       "font/ttf",
	"eot":       "application/vnd.ms-fontobject",
	"otf":       "font/otf",
	"txt":       "text/plain",
	"md":        "text/markdown",
	"mp4":       "video/mp4",
	"m4v":       "video/mp4",
	"f4v":       "video/mp4",
	"f4p":       "video/mp4",
	"m4p":       "video/mp4",
	"webm":      "video/webm",
	"ogg":       "video/ogg",
	"ogv":       "video/ogg",
	"wav":       "audio/wav",
	"mp3":       "audio/mpeg",
	"f4a":       "audio/mpeg",
	"m4b":       "audio/mpeg",
	"m4a":       "audio/m4a",
	"aac":       "audio/aac",
	"oga":       "audio/ogg",
	"spx":       "audio/ogg",
	"opus":      "audio/ogg",
	"webp":      "image/webp",
	"avif":      "image/avif",
	"gif":       "image/gif",
	"jpg":       "image/jpeg",
	"jpeg":      "image/jpeg",
	"jpe":       "image/jpeg",
	"jif":       "image/jpeg",
	"jfif":      "image/jpeg",
	"jfi":       "image/jpeg",
	"bmp":       "image/bmp",
	"tiff":      "image/tiff",
	"tif":       "image/tiff",
	"apng":      "image/apng",
	"psd":       "image/vnd.adobe.photoshop",
	"pdf":       "application/pdf",
	"zip":       "application/zip",
	"tar":       "application/x-tar",
	"gz":        "application/gzip",
	"bz2":       "application/x-bzip2",
	"7z":        "application/x-7z-compressed",
	"rar":       "application/x-rar-compressed",
	"xz":        "application/x-xz",
	"exe":       "application/x-msdownload",
	"swf":       "application/x-shockwave-flash",
	"flv":       "video/x-flv",
	"wmv":       "video/x-ms-wmv",
	"avi":       "video/x-msvideo",
	"mkv":       "video/x-matroska",
	"mov":       "video/quicktime",
	"mpg":       "video/mpeg",
	"mpeg":      "video/mpeg",
	"m3u8":      "application/x-mpegURL",
	"ts":        "video/MP2T",
	"m4s":       "video/MP2T",
	"xml":       "text/xml",
	"appcache":  "text/cache-manifest",
	"manifest":  "text/cache-manifest",
	"pac":       "application/x-ns-proxy-autoconfig",
	"ogx":       "application/ogg",
	"flac":      "audio/flac",
	"weba":      "audio/webm",
	"m3u":       "audio/x-mpegurl",
	"ai":        "application/postscript",
	"eps":       "application/postscript",
	"ps":        "application/postscript",
	"doc":       "application/msword",
	"docx":      "application/msword",
	"rtf":       "application/rtf",
	"xls":       "application/vnd.ms-excel",
	"xlsx":      "application/vnd.ms-excel",
	"ppt":       "application/vnd.ms-powerpoint",
	"pptx":      "application/vnd.ms-powerpoint",
	"rpm":       "application/x-redhat-package-manager",
	"deb":       "application/x-deb",
}

// 根据下载文件类型添加头部信息 - 优化版本，使用map查找，时间复杂度O(1)
func HTTPAddHeaderForSuffix(w http.ResponseWriter, suffix string) {
	if contentType, ok := contentTypeMap[suffix]; ok {
		w.Header().Set("Content-Type", contentType)
	} else {
		w.Header().Set("Content-Type", "application/octet-stream")
	}
}
